import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import linregress
import matplotlib.ticker as ticker

df = pd.read_csv("datos_dispersion_material.csv")

"""
lado_izq = df["lado izquierdo"]
lado_dch = df["lado derecho"]
color = df["color"]
angl_refr = df["angulo refractado"]
angl_incd = df["angulo incidente"]
"""

def grados_a_decimal(dato_str):
    if not isinstance(dato_str, str):
        return dato_str
    
    grados, minutos = map(float, dato_str.split('º'))
    return grados + (minutos / 60)

def decimal_a_grados(valor_decimal):
    grados = int(valor_decimal)
    
    parte_decimal = abs(valor_decimal - grados)
    minutos_float = parte_decimal * 60
    minutos = int(minutos_float)
    
    segundos = int(round((minutos_float - minutos) * 60, 2))
    
    return f"{grados}º {minutos}` {segundos}\""

def n_lambda(delta_m, alpha):
    return (np.sin(np.deg2rad((delta_m+alpha)/2)))/(np.sin(np.deg2rad(alpha/2)))


df['lado izquierda decimal'] = df['lado izquierdo'].apply(grados_a_decimal)
df['lado derecho decimal'] = df['lado derecho'].apply(grados_a_decimal)
df['angulo refractado decimal'] = df['angulo refractado'].apply(grados_a_decimal)
df['angulo incidente decimal'] = df['angulo incidente'].apply(grados_a_decimal)

# Calcular alpha
df["alpha"] = 180-(-df['lado izquierda decimal']+df['lado derecho decimal'])

alpha_media = df["alpha"].mean()
alpha_std = df["alpha"].std(ddof=1)

print(f"alpha = {decimal_a_grados(alpha_media)} +- {decimal_a_grados(alpha_std)} \t \t ({alpha_media:.2f} +- {alpha_std:.2f})")
print("-"*75)

# Calcular delta_m
naranja = df[df["color"] == "naranja"]
rojo = df[df["color"] == "rojo"]
verde = df[df["color"] == "verde"]
azul2 = df[df["color"] == "azul 2"]

s_delta = 0.18  # 1 minuto
delta_naranja = (naranja["angulo incidente"].apply(grados_a_decimal)-naranja["angulo refractado"].apply(grados_a_decimal)).mean()
delta_rojo = (rojo["angulo incidente"].apply(grados_a_decimal)-rojo["angulo refractado"].apply(grados_a_decimal)).mean()
delta_verde = (verde["angulo incidente"].apply(grados_a_decimal)-verde["angulo refractado"].apply(grados_a_decimal)).mean()
delta_azul2 = (azul2["angulo incidente"].apply(grados_a_decimal)-azul2["angulo refractado"].apply(grados_a_decimal)).mean()

print(f"d_m(naranja) = {decimal_a_grados(delta_naranja)} +- {decimal_a_grados(s_delta)} \t \t ({delta_naranja:.2f} +- {s_delta:.2f})")
print(f"d_m(rojo) = {decimal_a_grados(delta_rojo)} +- {decimal_a_grados(s_delta)} \t \t ({delta_rojo:.2f} +- {s_delta:.2f})")
print(f"d_m(verde) = {decimal_a_grados(delta_verde)} +- {decimal_a_grados(s_delta)} \t \t ({delta_verde:.2f} +- {s_delta:.2f})")
print(f"d_m(azul2) = {decimal_a_grados(delta_azul2)} +- {decimal_a_grados(s_delta)} \t \t ({delta_azul2:.2f} +- {s_delta:.2f})")
print("-"*75)

# Calcular n(lambda)
s_n = 0.002

n_nar  = n_lambda(delta_naranja, alpha_media)
n_roj  = n_lambda(delta_rojo,alpha_media)
n_ver  = n_lambda(delta_verde,alpha_media)
n_az2  = n_lambda(delta_azul2,alpha_media)
 
print(f"n(naranja) = {n_nar:.3f} +- {s_n}")
print(f"n(rojo)    = {n_roj:.3f} +- {s_n}")
print(f"n(verde)   = {n_ver:.3f} +- {s_n}")
print(f"n(azul2)   = {n_az2:.3f} +- {s_n}")
print("-" * 75)

# Graficos y cosas
wavelengths = {
    "naranja": 589,
    "rojo":    616,
    "verde":   568, # o 515 sino
    "azul 2":  475,
}
colores_plot = {
    "naranja": "#E8650A",
    "rojo":    "#CC2222",
    "verde":   "#22AA44",
    "azul 2":  "#4488EE",
}
 
datos = [
    ("naranja", wavelengths["naranja"], n_nar),
    ("rojo",    wavelengths["rojo"],    n_roj),
    ("verde",   wavelengths["verde"],   n_ver),
    ("azul 2",  wavelengths["azul 2"],  n_az2),
]
datos.sort(key=lambda x: x[1])  # orden creciente de λ
 
lam_nm = np.array([d[1] for d in datos], dtype=float)
n_vals = np.array([d[2] for d in datos], dtype=float)
x_cauchy = 1.0 / (lam_nm * 1e-9)**2   # 1/λ² en m⁻²
 
# Ajuste lineal de Cauchy: n = A + B·(1/λ²)
slope, intercept, r, p, se = linregress(x_cauchy, n_vals)
A_fit, B_fit = intercept, slope
R2 = r**2
 
print(f"Ajuste Cauchy:  n = A + B/λ²")
print(f"  r² = {R2:.6f}")

n_pts  = len(x_cauchy)
x_mean = x_cauchy.mean()
Sxx    = np.sum((x_cauchy - x_mean)**2)
s2_res = np.sum((n_vals - (A_fit + B_fit * x_cauchy))**2) / (n_pts - 2)
sB = np.sqrt(s2_res / Sxx)
sA = np.sqrt(s2_res * np.sum(x_cauchy**2) / (n_pts * Sxx))
 
print(f"  A = {A_fit:.6f}  +-  {sA:.6f}")
print(f"  B = {B_fit:.4e}  +-  {sB:.4e}  m²")

print("-" * 75)

 
BG   = "white"
GRID = "#dddddd"
ACC  = "#0077BB"
"""
BG   = "#0d1117"
GRID = "#1e2530"
ACC  = "#00FFCC"
"""
 
plt.rcParams.update({
    "font.family":       "monospace",
    "axes.labelcolor":   "black",
    "xtick.color":       "black",
    "ytick.color":       "black",
    "text.color":        "black",
})
 
# Grafica n vs 1/lambda**2
fig1, ax1 = plt.subplots(figsize=(7.5, 5))
fig1.patch.set_facecolor(BG)
ax1.set_facecolor(BG)
 
x_fit  = np.linspace(x_cauchy.min() * 0.93, x_cauchy.max() * 1.07, 400)
ax1.plot(x_fit, A_fit + B_fit * x_fit,
         color=ACC, lw=1.8, zorder=2,
         label=f"Cauchy:  n = {A_fit:.4f} + {B_fit:.3e}·(1/λ²)\n$R^2$ = {R2:.5f}")
 
for nombre, lam, n_val in datos:
    c = colores_plot[nombre]
    ax1.errorbar(1.0 / (lam * 1e-9)**2, n_val, yerr=s_n,
                 fmt="o", color=c, ecolor=c,
                 capsize=5, capthick=1.2, ms=7, lw=1.2,
                 label=f"{nombre}  (λ = {lam:.0f} nm)", zorder=3)
 
ax1.set_xlabel("1/λ²  (m⁻²)", fontsize=12, labelpad=8)
ax1.set_ylabel("n(λ)", fontsize=12, labelpad=8)
ax1.set_title("Ajuste de Cauchy", fontsize=13, pad=12)
ax1.legend(fontsize=8.5, facecolor="#161b27", labelcolor="white",
           edgecolor="#333", framealpha=0.9)
for sp in ax1.spines.values():
    sp.set_edgecolor("#333")
ax1.grid(True, color=GRID, linestyle="--", linewidth=0.7)
ax1.xaxis.set_major_formatter(ticker.ScalarFormatter(useMathText=True))
ax1.ticklabel_format(style="sci", axis="x", scilimits=(0, 0))
ax1.xaxis.get_offset_text().set_color("white")
 
plt.tight_layout()
plt.savefig("cauchy_fit.png", dpi=150, bbox_inches="tight", facecolor=BG)
print("Guardado: cauchy_fit.png")
plt.show()
 
# Gráfica n vs lambda
fig2, ax2 = plt.subplots(figsize=(7.5, 5))
fig2.patch.set_facecolor(BG)
ax2.set_facecolor(BG)
 
lam_curve = np.linspace(lam_nm.min() * 0.90, lam_nm.max() * 1.10, 500)
n_curve   = A_fit + B_fit / (lam_curve * 1e-9)**2
ax2.plot(lam_curve, n_curve,
         color=ACC, lw=1.8, zorder=2,
         label=f"Cauchy ajustado\n$R^2$ = {R2:.5f}")
 
for nombre, lam, n_val in datos:
    c = colores_plot[nombre]
    ax2.errorbar(lam, n_val, yerr=s_n,
                 fmt="o", color=c, ecolor=c,
                 capsize=5, capthick=1.2, ms=7, lw=1.2,
                 label=f"{nombre}  (λ = {lam:.0f} nm)", zorder=3)
 
ax2.set_xlabel("λ  (nm)", fontsize=12, labelpad=8)
ax2.set_ylabel("n(λ)", fontsize=12, labelpad=8)
ax2.set_title("n(λ)", fontsize=13, pad=12)
ax2.legend(fontsize=8.5, facecolor="#161b27", labelcolor="white",
           edgecolor="#333", framealpha=0.9)
for sp in ax2.spines.values():
    sp.set_edgecolor("#333")
ax2.grid(True, color=GRID, linestyle="--", linewidth=0.7)
 
plt.tight_layout()
plt.savefig("n_vs_lambda.png", dpi=150, bbox_inches="tight", facecolor=BG)
print("Guardado: n_vs_lambda.png")
plt.show()


#.describe()
#.mean()

# LEER EL INFORME Y ESTRUCTURAR QUE HACER Y QUE DATOS ANALIZAR
# HACER ANALISIS DATOS